package com.galudisu

import java.nio.file.{Files, Path, Paths}
import java.nio.file.StandardOpenOption
import java.nio.file.StandardOpenOption._
import java.time.ZonedDateTime

import scala.concurrent.duration._
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.util.{Failure, Success}
import akka.Done
import akka.actor._
import akka.util.ByteString
import akka.stream.{ActorAttributes, ActorMaterializer, IOResult}
import akka.stream.scaladsl.{BidiFlow, FileIO, Flow, Framing, Keep, Sink, Source}
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server._
import spray.json._

/**
  * @author galudisu
  */
class LogsApi (

  val logsDir: Path,
  val maxLine: Int
)(
  implicit val executionContext: ExecutionContext,
  val materializer: ActorMaterializer
) extends EventMarshalling {
  def logFile(id:String) = logsDir.resolve(id)
  // route logic follows...

  val inFlow = Framing.delimiter(ByteString("\n"), maxLine)
               .map(_.decodeString("UTF8"))
               .map(LogStreamProcessor.parseLineEx)
               .collect { case Some(e) => e }

  val outFlow = Flow[Event].map { event =>
    ByteString(event.toJson.compactPrint)
  }
  val bidiFlow = BidiFlow.fromFlows(inFlow, outFlow)

  import java.nio.file.StandardOpenOption
  import java.nio.file.StandardOpenOption._

  val logToJsonFlow = bidiFlow.join(Flow[Event])

  def logFileSink(logId:String) =
    FileIO.toPath(logFile(logId), Set(CREATE, WRITE, APPEND))
  def logFileSource(logId:String) =
    FileIO.fromPath(logFile(logId))

  def routes: Route = postRoute ~ getRoute ~ deleteRoute

  def postRoute =
    pathPrefix("logs" / Segment) { logId =>
      pathEndOrSingleSlash {
        post {
          entity(as[HttpEntity]) { entity =>                          // 提取HttpRequest内容
            onComplete(
              entity
              .dataBytes                                              // 数据类型 `Source[ByteString,Any]`
              .via(logToJsonFlow)                                     // protocol flow: log format in, json out
              .toMat(logFileSink(logId))(Keep.right)                  // write the JSON to the file
              .run()
            ) {
              case Success(IOResult(count, Success(Done))) =>         // 响应内容 `LogReceipt(logId,count)`
                complete((StatusCodes.OK, LogReceipt(logId, count)))
              case Success(IOResult(count, Failure(e))) =>            // 失败时的响应内容
                complete((
                  StatusCodes.BadRequest,
                  ParseError(logId, e.getMessage)
                ))
              case Failure(e) =>
                complete((
                  StatusCodes.BadRequest,
                  ParseError(logId, e.getMessage)
                ))
            }
          }
        }
      }
    }

  def getRoute =
    pathPrefix("logs" / Segment) { logId =>
      pathEndOrSingleSlash {
        get {
          if(Files.exists(logFile(logId))) {
            val src = logFileSource(logId)                            // 文件存在，则创建一个`Source[ByteString,Future[IOResult]]`
            complete(HttpEntity(ContentTypes.`application/json`, src) // 返回JSON content-type
            )
          } else {
            complete(StatusCodes.NotFound)
          }
        }
      }
    }

  def deleteRoute =
    pathPrefix("logs" / Segment) { logId =>
      pathEndOrSingleSlash {
        delete {
          if(Files.deleteIfExists(logFile(logId))) complete(StatusCodes.OK)
          else complete(StatusCodes.InternalServerError)
        }
      }
    }
}



























